#include <memory.h>

#include "land_base.h"
#include "land_graphics.h"

#define LAND_2D_CORE

#include "init.h"

typedef struct {
	LandVector rect;
	LandVector uv;
	unsigned count;
	LandTexture* texture;
}TGlyph;

struct LandLabel {
	LandObj obj;
	char* str;
	size_t len;
	unsigned fsize;
	LandFont* font;
	int dirty;
	unsigned offset;
	unsigned count;
	LandVector gap;
	LandList* glyphList;
};

static struct {
	unsigned texPos;
	LandDraw draw;
	LandTexture* texture;
}ctx = { 0 };


static void
remakeGlyphList(LandLabel* label) {
	landClearList(label->glyphList);
	if (!label->str) {
		return;
	}
	LandVector* rect = &label->obj.rect;
	LandVector* color = &label->obj.color;
	uint32_t ch;
	unsigned offset = 0;
	LandFloat xoffset = rect->right * (-rect->left);
	LandFloat yoffset = rect->bottom * (-rect->top);
	LandFloat xpos = xoffset;
	LandFloat ypos = yoffset;
	unsigned step = 0;
	unsigned pos = 0;
	LandGlyph glyph;
	TGlyph tg;
	LandVector vr;
	while (ch = landUtf8(label->str + pos, &step)) {
		pos += step;
		if (ch == '\n') {
			ypos += (LandFloat)label->fsize + label->gap.y;
			xpos = xoffset;
			if(ypos >= rect->bottom){
				break;
			}
			continue;
		}
		else if (ch == '\r') {
			continue;
		}
		landRegFont(label->font, ch, label->fsize, &glyph);
		vr.left = xpos + (LandFloat)glyph.x;
		vr.top = ypos + (LandFloat)glyph.y;
		vr.right = vr.left + (LandFloat)glyph.width;
		vr.bottom = vr.top + (LandFloat)glyph.height;
		xpos += glyph.fw + label->gap.x;
		if (xpos >= rect->right) {
			break;
		}
		tg.rect = vr;
		tg.texture = glyph.texture;
		tg.uv = glyph.uv;
		landPushList(label->glyphList, &tg, 1);
		if (pos >= label->len) {
			break;
		}
	}
}

static void
beginDraw(LandScene* scene) {
	LandDraw* draw = &ctx.draw;
	landClearVertex(draw->vertex);
	draw->offset = 0;
	draw->count = 0;
	draw->view = &scene->view;
}

static void
readyDraw(LandScene* scene) {
	landUploadVertex(ctx.draw.vertex);
}

static void
preDraw(LandScene* scene, LandObj* obj) {
	LandLabel* label = (LandLabel*)obj;
	LandVector* color = &label->obj.color;
	if (label->dirty) {
		remakeGlyphList(label);
		label->dirty = 0;
	}
	LandList* glyphList = label->glyphList;
	if (!landGetListTotal(glyphList)) {
		return;
	}
	LandDraw* draw = &ctx.draw;
	LandVertex* vertex = draw->vertex;
	TGlyph* tgs;
	TGlyph* tg;
	unsigned count;
	unsigned pointPos = landGetListTotal(vertex->points);
	label->offset = landGetListTotal(vertex->indexs);
	landBeginList(glyphList);
	while (landNextList(glyphList, &tgs, &count)) {
		for (unsigned i = 0; i < count; i++) {
			tg = &tgs[i];
			tg->count = landPushObjRect(vertex,
				&tg->rect, &tg->uv,
				color, &pointPos, &obj->transform);
		}
	}
}

static void
draw(LandScene* scene, LandObj* obj) {
	LandLabel* label = (LandLabel*)obj;
	LandList* glyphList = label->glyphList;
	LandDraw* draw = &ctx.draw;
	draw->view = &scene->view;
	if (scene->draw) {
		if (scene->draw != draw) {
			landDraw(scene->canvas, scene->draw);
			scene->draw = NULL;
			ctx.texture = NULL;
		}
	}
	if (!landGetListTotal(glyphList)) {
		return;
	}
	LandVertex* vertex = draw->vertex;
	TGlyph* tgs;
	TGlyph* tg;
	unsigned count;
	unsigned offset = label->offset;
	landBeginList(glyphList);
	while (landNextList(glyphList, &tgs, &count)) {
		for (unsigned i = 0; i < count; i++) {
			tg = &tgs[i];
			if (ctx.texture == tg->texture) {
				draw->count += tg->count;
			}
			else {
				if (ctx.texture) {
					landDraw(scene->canvas, draw);
				}
				ctx.texture = tg->texture;
				draw->offset = offset;
				draw->count = tg->count;
				landUniformTex(draw, ctx.texPos, tg->texture);
			}
			offset += tg->count;
		}
	}
	if (ctx.texture) {
		scene->draw = draw;
	}
}

static void
visitLabel(LandObj* obj, LandMatrix2D* m, int force) {
	LandLabel* label = (LandLabel*)obj;
	int flag = LAND_2D_OBJ_DIRTY_ANCHOR | LAND_2D_OBJ_DIRTY_SIZE;
	if ((obj->dirty & flag) > 0) {
		label->dirty = 1;
	}
}

static void
freeLabel(LandObj* obj) {
	LandLabel* label = (LandLabel*)obj;
	if (label->str) {
		free(label->str);
	}
	landReleaseRef(label->font);
}

LAND_2D_API LandLabel*
landNewLabel(LandFont* font, unsigned fsize) {
	landRetainRef(font);
	LandLabel* label = landNewExtendObj(sizeof(LandLabel), freeLabel);
	label->font = font;
	label->fsize = fsize;
	label->dirty = 0;
	label->obj.visit = visitLabel;
	label->obj.preDraw = preDraw;
	label->obj.draw = draw;
	label->glyphList = landNewList(sizeof(TGlyph), 16);
	return label;
}

LAND_2D_API void
landLabelStr(LandLabel* p, const char* str, size_t len) {
	LandLabel* label = (LandLabel*)p;
	if (p->str) {
		free(p->str);
	}
	p->str = NULL;
	p->len = len;
	if (len) {
		p->str = malloc(len + 1);
		memcpy(p->str, str, len);
		p->str[len] = 0;
	}
	label->dirty = 1;
}

LAND_2D_API void
landLabelFontSize(LandLabel* label, unsigned fsize) {
	if (fsize < 8) {
		fsize = 8;
	}
	if (label->fsize == fsize) {
		return;
	}
	label->fsize = fsize;
	label->dirty = 1;
}

int
landInitLabelContext() {
	LandDraw* draw = &ctx.draw;
	draw->depthTest = LAND_DEPTH_TEST_ALWAYS;
	draw->drawType = LAND_DRAW_TRIANGLES;
	draw->shader = landNewStdShader(LAND_STD_SHADER_APLHA_TEXTURE);
	draw->vertex = landNewVertex(128);
	landRetainRef(draw->shader);
	landRetainRef(draw->vertex);
	ctx.texPos = landRegUniformTex(draw, "landBaseTexture", NULL);
	LandSceneMod mod = {
		beginDraw,
		readyDraw,
	};
	landPushSceneMod(&mod);
	return 0;
}